﻿namespace VisualRegExWpf
{
    using Layout.Sugiyama;
    using Layout.Geometry;
    using FiniteAutomaton;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Shapes;

    using Petzold.Media2D;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void ParseButton_Click(object sender, RoutedEventArgs e)
        {
            canvas.Children.Clear();
            Parser parser = new Parser();
            NondeterministicFiniteAutomaton nfa = parser.CreateNFA(this.inputTextBox.Text);
            if (convertToDFA.IsChecked.HasValue && convertToDFA.IsChecked.Value)
            {
                nfa = Converter.ConvertNFAToDFA(nfa);
            }

            SugiyamaLayoutAlgorithm<State, LabeledEdge<State, char>> algo = new SugiyamaLayoutAlgorithm<State, LabeledEdge<State, char>>(nfa);
            Dictionary<State, VertexControl> stateToVertexControlMap = new Dictionary<State, VertexControl>();
            int i = 0;

            foreach (State s in nfa.States)
            {
                VertexControl vc = new VertexControl() { Content = (++i).ToString() };
                stateToVertexControlMap[s] = vc;
                canvas.Children.Add(vc);
                System.Windows.Size size = new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity);
                vc.Measure(size);
                System.Windows.Size desiredSize = vc.DesiredSize;

                Layout.Geometry.Size layoutSize = new Layout.Geometry.Size(desiredSize.Height, desiredSize.Width);
                algo.VertexSizes[s] = layoutSize;
            }

            algo.LayerPadding = 50;
            algo.VertexPadding = 60;
            algo.StartLayout();

            foreach (State s in nfa.States)
            {
                VertexControl vc = stateToVertexControlMap[s];
                Canvas.SetLeft(vc, algo.VertexPositions[s].Y - vc.DesiredSize.Width / 2);
                Canvas.SetTop(vc, algo.VertexPositions[s].X - vc.DesiredSize.Height / 2);
            }

            foreach (LabeledEdge<State, char> transition in nfa.Transitions)
            {
                // Line segments
                List<Layout.Geometry.Point> segments = algo.EdgeSegments[transition];

                ArrowPolyline line = new ArrowPolyline();
                line.StrokeThickness = 2;
                line.Stroke = Brushes.Black;
                foreach (Layout.Geometry.Point p in segments)
                {
                    line.Points.Add(new System.Windows.Point(p.Y, p.X));
                }

                canvas.Children.Add(line);
                Canvas.SetZIndex(line, 0);

                // Label
                LabelControl label = new LabelControl()
                {
                    Content = transition.Label == (char) 0 ? "ε" : transition.Label.ToString()
                };

                canvas.Children.Add(label);
                System.Windows.Point labelPosition = new System.Windows.Point();
                if (segments.Count % 2 == 0)
                {
                    labelPosition.Y = (segments[segments.Count / 2].X + segments[segments.Count / 2 - 1].X) / 2;
                    labelPosition.X = (segments[segments.Count / 2].Y + segments[segments.Count / 2 - 1].Y) / 2;
                }
                else
                {
                    labelPosition.Y = segments[segments.Count / 2].X;
                    labelPosition.X = segments[segments.Count / 2].Y;
                }

                System.Windows.Size size = new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity);
                label.Measure(size);
                System.Windows.Size desiredSize = label.DesiredSize;
                Canvas.SetLeft(label, labelPosition.X - desiredSize.Width / 2);
                Canvas.SetTop(label, labelPosition.Y - desiredSize.Height / 2);
                Canvas.SetZIndex(label, 1);
            }
        }
    }
}
